home *** CD-ROM | disk | FTP | other *** search
/ BBS Toolkit / BBS Toolkit.iso / doors_2 / ziptv21.zip / ZIPTV.PAS < prev   
Pascal/Delphi Source File  |  1990-04-22  |  37KB  |  1,535 lines

  1.  
  2. (*
  3.  * Copyright 1987, 1990 Samuel H. Smith;  All rights reserved
  4.  *
  5.  * This is a component of the ProDoor System.
  6.  * Do not distribute modified versions without my permission.
  7.  * Do not remove or alter this notice or any other copyright notice.
  8.  * If you use this in your own program you must distribute source code.
  9.  * Do not use any of this in a commercial product.
  10.  *
  11.  *)
  12.  
  13. (*
  14.  * ZipTV - zipfile text view utility/door
  15.  *
  16.  *)
  17.  
  18. {$I prodef.inc}
  19.  
  20. {$M 5000,0,0} {minstack,minheap,maxheap}
  21.  
  22. {$D+}    {Global debug information}
  23. {$L+}    {Local debug information}
  24.  
  25. { $r+,s+}
  26.  
  27. program ZipTV;
  28.  
  29. Uses
  30.    Dos, DosMem, MiniCrt, Mdosio, Tools, CInput;
  31.  
  32. const
  33.    version = 'ZipTV:  ZIP Text Viewer v2.1 of 04-22-90;  (C) 1990 S.H.Smith';
  34.  
  35.  
  36. (* ----------------------------------------------------------- *)
  37. (*
  38.  * ZIPfile layout declarations
  39.  *
  40.  *)
  41.  
  42. type
  43.    signature_type = longint;
  44.  
  45. const
  46.    local_file_header_signature = $04034b50;
  47.  
  48. type
  49.    local_file_header = record
  50.       version_needed_to_extract:    word;
  51.       general_purpose_bit_flag:     word;
  52.       compression_method:           word;
  53.       last_mod_file_time:           word;
  54.       last_mod_file_date:           word;
  55.       crc32:                        longint;
  56.       compressed_size:              longint;
  57.       uncompressed_size:            longint;
  58.       filename_length:              word;
  59.       extra_field_length:           word;
  60.    end;
  61.  
  62. const
  63.    {general_purpose_bit_flag bit values}
  64.    GP_encrypted   = 1;     {file is encrypted}
  65.    GP_8K_dict     = 2;     {8k implode dictionary}
  66.    GP_lit_tree    = 4;     {literal implode tree is present}
  67.  
  68.  
  69. const
  70.    central_file_header_signature = $02014b50;
  71.  
  72. type
  73.    central_directory_file_header = record
  74.       version_made_by:                 word;
  75.       version_needed_to_extract:       word;
  76.       general_purpose_bit_flag:        word;
  77.       compression_method:              word;
  78.       last_mod_file_time:              word;
  79.       last_mod_file_date:              word;
  80.       crc32:                           longint;
  81.       compressed_size:                 longint;
  82.       uncompressed_size:               longint;
  83.       filename_length:                 word;
  84.       extra_field_length:              word;
  85.       file_comment_length:             word;
  86.       disk_number_start:               word;
  87.       internal_file_attributes:        word;
  88.       external_file_attributes:        longint;
  89.       relative_offset_local_header:    longint;
  90.    end;
  91.  
  92. const
  93.    end_central_dir_signature = $06054b50;
  94.  
  95. type
  96.    end_central_dir_record = record
  97.       number_this_disk:                         word;
  98.       number_disk_with_start_central_directory: word;
  99.       total_entries_central_dir_on_this_disk:   word;
  100.       total_entries_central_dir:                word;
  101.       size_central_directory:                   longint;
  102.       offset_start_central_directory:           longint;
  103.       zipfile_comment_length:                   word;
  104.    end;
  105.  
  106. const
  107.    compression_methods: array[0..7] of string[8] =
  108.       (' Stored ',
  109.        ' Shrunk ',
  110.        'Reduce-1', 'Reduce-2', 'Reduce-3', 'Reduce-4',
  111.        'Imploded',
  112.        '    ?   ');
  113.  
  114.  
  115. (* ----------------------------------------------------------- *)
  116. (*
  117.  * input file variables
  118.  *
  119.  *)
  120.  
  121. const
  122.    uinbufsize = 512;    {input buffer size}
  123.  
  124. var
  125.    zipeof:     boolean;
  126.  
  127.    csize:      longint;
  128.    cusize:     longint;
  129.    cmethod:    integer;
  130.    cflags:     word;
  131.  
  132.    inbuf:      array[1..uinbufsize] of byte;
  133.    inpos:      integer;
  134.    incnt:      integer;
  135.    pc:         byte;
  136.    pcbits:     byte;
  137.    pcbitv:     byte;
  138.    zipfd:      dos_handle;
  139.    zipfn:      dos_filename;
  140.  
  141.  
  142.  
  143. (* ----------------------------------------------------------- *)
  144. (*
  145.  * output stream variables
  146.  *
  147.  *)
  148.  
  149. const
  150.    hsize =     8192;    {must be 8192 for 13 bit shrinking}
  151.  
  152.    max_binary = 50;     {non-printing count before binary file trigger}
  153.    max_linelen = 200;   {line length before binary file triggered}
  154.  
  155.    maxlines: integer = 500;
  156.                         {maximum lines per session}
  157.  
  158. var
  159.    uoutbuf:             string[max_linelen];    {disp line buffer}
  160.    binary_count:        integer;                {non-text chars so far}
  161.  
  162.    outbuf:              array[0..hsize] of byte; {must be >= 8192 for look-back}
  163.    outpos:              longint;                 {absolute position in outfile}
  164.  
  165.  
  166. (* ----------------------------------------------------------- *)
  167. (*
  168.  * other working storage
  169.  *
  170.  *)
  171.  
  172. var
  173.    expand_files:        boolean;
  174.    header_present:      boolean;
  175.    default_pattern:     string20;
  176.    pattern:             string20;
  177.    action:              string20;
  178.  
  179.  
  180.  
  181. (* ----------------------------------------------------
  182.  *
  183.  *  Zipfile input/output handlers
  184.  *
  185.  *)
  186.  
  187. procedure skip_rest;
  188. begin
  189.    dos_lseek(zipfd,csize-incnt,seek_cur);
  190.    zipeof := true;
  191.    csize := 0;
  192.    incnt := 0;
  193. end;
  194.  
  195. procedure skip_csize;
  196. begin
  197.    incnt := 0;
  198.    skip_rest;
  199. end;
  200.  
  201.  
  202. (* ------------------------------------------------------------- *)
  203. procedure ReadByte(var x: byte);
  204. begin
  205.    if incnt = 0 then
  206.    begin
  207.       if csize = 0 then
  208.       begin
  209.          zipeof := true;
  210.          exit;
  211.       end;
  212.  
  213.       inpos := sizeof(inbuf);
  214.       if inpos > csize then
  215.          inpos := csize;
  216.       incnt := dos_read(zipfd,inbuf,inpos);
  217.  
  218.       inpos := 1;
  219.       dec(csize,incnt);
  220.    end;
  221.  
  222.    x := inbuf[inpos];
  223.    inc(inpos);
  224.    dec(incnt);
  225. end;
  226.  
  227.  
  228. (* ------------------------------------------------------------- *)
  229. procedure ReadBits(bits: integer; var x: integer);
  230.    {read the specified number of bits}
  231. var
  232.    bit:     integer;
  233.    bitv:    integer;
  234.  
  235. begin
  236.  
  237. (****
  238. write('readbits n=',bits,' b=');
  239. ****)
  240.  
  241.    x := 0;
  242.    bitv := 1;
  243.  
  244.    for bit := 0 to bits-1 do
  245.    begin
  246.  
  247.       if pcbits > 0 then
  248.       begin
  249.          dec(pcbits);
  250.          pcbitv := pcbitv shl 1;
  251.       end
  252.       else
  253.  
  254.       begin
  255.          ReadByte(pc);
  256.          pcbits := 7;
  257.          pcbitv := 1;
  258.       end;
  259.  
  260.       if (pc and pcbitv) <> 0 then
  261.          x := x or bitv;
  262.  
  263.       bitv := bitv shl 1;
  264.    end;
  265.  
  266. (****
  267. writeln(' -> ',x,' = ',binary(x));
  268. *****)
  269.  
  270. end;
  271.  
  272.  
  273. (* ---------------------------------------------------------- *)
  274. procedure get_string(len: integer; var s: string);
  275. var
  276.    n: integer;
  277. begin
  278.    if len <= 255 then
  279.       n := dos_read(zipfd,s[1],len)
  280.    else
  281.    begin
  282.       n := dos_read(zipfd,s[1],255);
  283.       dos_lseek(zipfd,len-255,seek_cur);
  284.       len := 255;
  285.    end;
  286.  
  287.    s[0] := chr(len);
  288. end;
  289.  
  290.  
  291. (* ------------------------------------------------------------- *)
  292. procedure OutByte (c: integer);
  293.    (* output each character from archive to screen *)
  294.  
  295.    procedure flushbuf;
  296.    begin
  297.       disp(uoutbuf);
  298.       uoutbuf := '';
  299.    end;
  300.  
  301.    procedure addchar;
  302.    begin
  303.       inc(uoutbuf[0]);
  304.       uoutbuf[length(uoutbuf)] := chr(c);
  305.    end;
  306.  
  307.    procedure not_text;
  308.    begin
  309.       newline;
  310.       displn('This is not a text file!');
  311.       linenum := 1000;
  312.       skip_rest;
  313.    end;
  314.    
  315. begin
  316.    outbuf[outpos mod sizeof(outbuf)] := c;
  317.    inc(outpos);
  318.  
  319. (********
  320. if debug then begin
  321. if c = 13 then else
  322. if c = 10 then begin
  323.    if nomore then
  324.       skip_rest
  325.    else
  326.       newline;
  327. end else write(chr(c));
  328. writeln(' [outbyte c=',c:3,' outpos=',outpos-1:5,']');
  329. if keypressed and (readkey=#27) then halt;
  330. exit; end;
  331. ********)
  332.  
  333.    case c of
  334.    10:  begin
  335.            if linenum < 1000 then
  336.            begin
  337.               flushbuf;
  338.               newline;
  339.  
  340.               dec(maxlines);
  341.               if (maxlines < 1) and (not dump_user) then
  342.               begin
  343.                   newline;
  344.                   displn('You''ve seen enough.  Please download this file if you want to see more.');
  345.                   dump_user := true;
  346.               end;
  347.            end;
  348.  
  349.            if nomore or dump_user then
  350.               skip_rest;
  351.         end;
  352.  
  353.    13:   ;
  354.  
  355.    26: begin
  356.           flushbuf;
  357.           skip_rest;         {jump to nomore mode on ^z}
  358.        end;
  359.  
  360.    8,9,32..255:
  361.        begin
  362.           if length(uoutbuf) >= max_linelen then
  363.           begin
  364.              flushbuf;
  365.              if csize > 10 then
  366.                 not_text;
  367.           end;
  368.  
  369.           if linenum < 1000 then   {stop display on nomore}
  370.              addchar;
  371.        end;
  372.  
  373.    else
  374.       begin
  375.          if binary_count < max_binary then
  376.             inc(binary_count)
  377.          else
  378.          if csize > 10 then
  379.             not_text;
  380.       end;
  381.    end;
  382.  
  383. end;
  384.  
  385.  
  386. (* ------------------------------------------------------------- *)
  387. (*
  388.  * The Reducing algorithm is actually a combination of two
  389.  * distinct algorithms.  The first algorithm compresses repeated
  390.  * byte sequences, and the second algorithm takes the compressed
  391.  * stream from the first algorithm and applies a probabilistic
  392.  * compression method.
  393.  *
  394.  *)
  395.  
  396. procedure unReduce;
  397.    {expand probablisticly reduced data}
  398.  
  399.    type
  400.       Sarray = array[0..255] of string[64];
  401.  
  402.    var
  403.       factor:     integer;
  404.       followers:  ^Sarray;
  405.       ExState:    integer;
  406.       C:          integer;
  407.       V:          integer;
  408.       Len:        integer;
  409.  
  410.    const
  411.       Lmask:   array[1..4] of integer = ($7f,$3f,$1f,$0f);
  412.       Fcase:   array[1..4] of integer = (127, 63, 31, 15);
  413.       Dshift:  array[1..4] of integer = (7,6,5,4);
  414.       Dand:    array[1..4] of integer = ($01,$03,$07,$0f);
  415.  
  416.  
  417.    procedure Expand(c: byte);
  418.    const
  419.       DLE = 144;
  420.    var
  421.       op:   longint;
  422.       i:    integer;
  423.  
  424.    begin
  425.  
  426.       case ExState of
  427.            0:  if C <> DLE then
  428.                    OutByte(C)
  429.                else
  430.                    ExState := 1;
  431.  
  432.            1:  if C <> 0 then
  433.                begin
  434.                    V := C;
  435.                    Len := V and Lmask[factor];
  436.                    if Len = Fcase[factor] then
  437.                      ExState := 2
  438.                    else
  439.                      ExState := 3;
  440.                end
  441.                else
  442.                begin
  443.                    OutByte(DLE);
  444.                    ExState := 0;
  445.                end;
  446.  
  447.            2:  begin
  448.                   inc(Len,C);
  449.                   ExState := 3;
  450.                end;
  451.  
  452.            3:  begin
  453.                   op := outpos - C - 1 - ((V shr Dshift[factor]) and
  454.                                           Dand[factor]) * 256;
  455.  
  456.                   for i := 0 to Len+2 do
  457.                   begin
  458.                      if op < 0 then
  459.                         OutByte(0)
  460.                      else
  461.                         OutByte(outbuf[op mod sizeof(outbuf)]);
  462.                      inc(op);
  463.                   end;
  464.  
  465.                   ExState := 0;
  466.                end;
  467.       end;
  468.    end;
  469.  
  470.  
  471.    procedure LoadFollowers;
  472.    var
  473.       x: integer;
  474.       i: integer;
  475.       b: integer;
  476.    begin
  477.       for x := 255 downto 0 do
  478.       begin
  479.          ReadBits(6,b);
  480.          followers^[x][0] := chr(b);
  481.  
  482.          for i := 1 to length(followers^[x]) do
  483.          begin
  484.             ReadBits(8,b);
  485.             followers^[x][i] := chr(b);
  486.          end;
  487.       end;
  488.    end;
  489.  
  490.  
  491.    function B(x: byte): word;
  492.       {number of bits needed to encode the specified number}
  493.    begin
  494.       case x-1 of
  495.          0..1:    B := 1;
  496.          2..3:    B := 2;
  497.          4..7:    B := 3;
  498.          8..15:   B := 4;
  499.         16..31:   B := 5;
  500.         32..63:   B := 6;
  501.         64..127:  B := 7;
  502.       else        B := 8;
  503.       end;
  504.    end;
  505.  
  506.  
  507. (* ----------------------------------------------------------- *)
  508. var
  509.    lchar:   integer;
  510.    lout:    integer;
  511.    I:       integer;
  512.    mem:     longint;
  513.  
  514. begin
  515.    mem := (sizeof(followers^)+100) - dos_maxavail;
  516.    if mem > 0 then
  517.    begin
  518.       displn(ltoa(mem)+' more bytes of RAM needed to UnReduce!');
  519.       skip_csize;
  520.       exit;
  521.    end;
  522.  
  523.    factor := cmethod - 1;
  524.    if (factor < 1) or (factor > 4) then
  525.    begin
  526.       skip_csize;
  527.       exit;
  528.    end;
  529.  
  530.    dos_getmem(followers,sizeof(followers^));
  531.    ExState := 0;
  532.    LoadFollowers;
  533.    lchar := 0;
  534.  
  535.    while (not zipeof) and (outpos < cusize) and (not dump_user) do
  536.    begin
  537.  
  538.       if followers^[lchar] = '' then
  539.          ReadBits( 8,lout )
  540.       else
  541.  
  542.       begin
  543.          ReadBits(1,lout);
  544.          if lout <> 0 then
  545.             ReadBits( 8,lout )
  546.          else
  547.          begin
  548.             ReadBits( B(length(followers^[lchar])), I );
  549.             lout := ord( followers^[lchar][I+1] );
  550.          end;
  551.       end;
  552.  
  553.       Expand( lout );
  554.       lchar := lout;
  555.    end;
  556.  
  557.    dos_freemem(followers);
  558. end;
  559.  
  560.  
  561.  
  562. (* ------------------------------------------------------------- *)
  563. (*
  564.  * UnShrinking
  565.  * -----------
  566.  *
  567.  * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
  568.  * with partial clearing.  The initial code size is 9 bits, and
  569.  * the maximum code size is 13 bits.  Shrinking differs from
  570.  * conventional Dynamic Ziv-lempel-Welch implementations in several
  571.  * respects:
  572.  *
  573.  * 1)  The code size is controlled by the compressor, and is not
  574.  *     automatically increased when codes larger than the current
  575.  *     code size are created (but not necessarily used).  When
  576.  *     the decompressor encounters the code sequence 256
  577.  *     (decimal) followed by 1, it should increase the code size
  578.  *     read from the input stream to the next bit size.  No
  579.  *     blocking of the codes is performed, so the next code at
  580.  *     the increased size should be read from the input stream
  581.  *     immediately after where the previous code at the smaller
  582.  *     bit size was read.  Again, the decompressor should not
  583.  *     increase the code size used until the sequence 256,1 is
  584.  *     encountered.
  585.  *
  586.  * 2)  When the table becomes full, total clearing is not
  587.  *     performed.  Rather, when the compresser emits the code
  588.  *     sequence 256,2 (decimal), the decompressor should clear
  589.  *     all leaf nodes from the Ziv-Lempel tree, and continue to
  590.  *     use the current code size.  The nodes that are cleared
  591.  *     from the Ziv-Lempel tree are then re-used, with the lowest
  592.  *     code value re-used first, and the highest code value
  593.  *     re-used last.  The compressor can emit the sequence 256,2
  594.  *     at any time.
  595.  *
  596.  *)
  597.  
  598. procedure unShrink;
  599.  
  600. const
  601.    max_bits =  13;
  602.    init_bits = 9;
  603.    first_ent = 257;
  604.    clear =     256;
  605.    
  606. type
  607.    hsize_array_integer = array[0..hsize] of integer;
  608.    hsize_array_byte    = array[0..hsize] of byte;
  609.  
  610. var
  611.    cbits:      integer;
  612.    maxcode:    integer;
  613.    free_ent:   integer;
  614.    maxcodemax: integer;
  615.    offset:     integer;
  616.    sizex:      integer;
  617.    prefix_of:  ^hsize_array_integer;
  618.    suffix_of:  ^hsize_array_byte;
  619.    stack:      hsize_array_byte absolute outbuf;
  620.    stackp:     integer;
  621.    finchar:    integer;
  622.    code:       integer;
  623.    oldcode:    integer;
  624.    incode:     integer;
  625.  
  626.  
  627.    (* ------------------------------------------------------------- *)
  628.    procedure partial_clear;
  629.    var
  630.       pr:   integer;
  631.       cd:   integer;
  632.  
  633.    begin
  634.       {mark all nodes as potentially unused}
  635.       for cd := first_ent to free_ent-1 do
  636.          word(prefix_of^[cd]) := prefix_of^[cd] or $8000;
  637.  
  638.  
  639.       {unmark those that are used by other nodes}
  640.       for cd := first_ent to free_ent-1 do
  641.       begin
  642.          pr := prefix_of^[cd] and $7fff;    {reference to another node?}
  643.          if pr >= first_ent then            {flag node as referenced}
  644.             prefix_of^[pr] := prefix_of^[pr] and $7fff;
  645.       end;
  646.  
  647.  
  648.       {clear the ones that are still marked}
  649.       for cd := first_ent to free_ent-1 do
  650.          if (prefix_of^[cd] and $8000) <> 0 then
  651.             prefix_of^[cd] := -1;
  652.  
  653.  
  654.       {find first cleared node as next free_ent}
  655.       free_ent := first_ent;
  656.       while (free_ent < maxcodemax) and (prefix_of^[free_ent] <> -1) do
  657.          inc(free_ent);
  658.    end;
  659.  
  660.  
  661.  
  662. (* ------------------------------------------------------------- *)
  663. var
  664.    mem:  longint;
  665. begin
  666.    mem := (sizeof(prefix_of^)+sizeof(suffix_of^)+ 100) - dos_maxavail;
  667.  
  668.    if mem > 0 then
  669.    begin
  670.       displn(ltoa(mem)+' more bytes of RAM needed to UnShrink!');
  671.       skip_csize;
  672.       exit;
  673.    end;
  674.  
  675.  
  676.    {allocate heap storage}
  677.    dos_getmem(prefix_of,sizeof(prefix_of^));
  678.    dos_getmem(suffix_of,sizeof(suffix_of^));
  679.  
  680.  
  681.    {decompress the file}
  682.    maxcodemax := 1 shl max_bits;
  683.    cbits := init_bits;
  684.    maxcode := (1 shl cbits)- 1;
  685.    free_ent := first_ent;
  686.    offset := 0;
  687.    sizex := 0;
  688.  
  689.    fillchar(prefix_of^,sizeof(prefix_of^),$FF);
  690.    for code := 255 downto 0 do
  691.    begin
  692.       prefix_of^[code] := 0;
  693.       suffix_of^[code] := code;
  694.    end;
  695.  
  696.    ReadBits(cbits,oldcode);
  697.    finchar := oldcode;
  698.    if zipeof then
  699.       exit;
  700.  
  701.    OutByte(finchar);
  702.  
  703.    stackp := 0;
  704.  
  705.    while (not zipeof) and (not dump_user) do
  706.    begin
  707.       ReadBits(cbits,code);
  708.  
  709.       while code = clear do
  710.       begin
  711.          ReadBits(cbits,code);
  712.  
  713.          case code of
  714.             1: begin
  715.                   inc(cbits);
  716.                   if cbits = max_bits then
  717.                      maxcode := maxcodemax
  718.                   else
  719.                      maxcode := (1 shl cbits) - 1;
  720.                end;
  721.  
  722.             2: partial_clear;
  723.          end;
  724.  
  725.          ReadBits(cbits,code);
  726.       end;
  727.  
  728.  
  729.       {special case for KwKwK string}
  730.       incode := code;
  731.       if prefix_of^[code] = -1 then
  732.       begin
  733.          stack[stackp] := finchar;
  734.          inc(stackp);
  735.          code := oldcode;
  736.       end;
  737.  
  738.  
  739.       {generate output characters in reverse order}
  740.       while (code >= first_ent) and (stackp < sizeof(stack)-1) do
  741.       begin
  742.          stack[stackp] := suffix_of^[code];
  743.          inc(stackp);
  744.          code := prefix_of^[code];
  745.       end;
  746.  
  747.       finchar := suffix_of^[code];
  748.       stack[stackp] := finchar;
  749.       inc(stackp);
  750.  
  751.  
  752.       {and put them out in forward order}
  753.       while (stackp > 0) do
  754.       begin
  755.          outpos := stackp; {required to preserve shared buffer/stack}
  756.          dec(stackp);
  757.          OutByte(stack[stackp]);
  758.       end;
  759.  
  760.  
  761.       {generate new entry}
  762.       code := free_ent;
  763.       if code < maxcodemax then
  764.       begin
  765.          prefix_of^[code] := oldcode;  {previous code}
  766.          suffix_of^[code] := finchar;  {final character from this code}
  767.          while (free_ent < maxcodemax) and (prefix_of^[free_ent] <> -1) do
  768.             inc(free_ent);
  769.       end;
  770.  
  771.  
  772.       {remember previous code}
  773.       oldcode := incode;
  774.    end;
  775.  
  776.  
  777.    {release heap storage}
  778.    dos_freemem(suffix_of);
  779.    dos_freemem(prefix_of);
  780. end;
  781.  
  782.  
  783. (* ------------------------------------------------------------- *)
  784. (*
  785.  * Imploding
  786.  * ---------
  787.  *
  788.  * The Imploding algorithm is actually a combination of two distinct
  789.  * algorithms.  The first algorithm compresses repeated byte sequences
  790.  * using a sliding dictionary.  The second algorithm is used to compress
  791.  * the encoding of the sliding dictionary ouput, using multiple
  792.  * Shannon-Fano trees.
  793.  *
  794.  *)
  795.  
  796. procedure unImplode;
  797.    {expand imploded data}
  798.  
  799.    const
  800.       maxSF = 256;
  801.  
  802.    type
  803.       sf_entry = record
  804.                     Code:       word;
  805.                     Value:      byte;
  806.                     BitLength:  byte;
  807.                  end;
  808.  
  809.       sf_tree = record  {a shannon-fano tree}
  810.          entry:         array[0..maxSF] of sf_entry;
  811.          entries:       integer;
  812.          MaxLength:     integer;
  813.       end;
  814.  
  815.       sf_treep = ^sf_tree;
  816.  
  817.    var
  818.       lit_tree:               sf_treep;
  819.       length_tree:            sf_treep;
  820.       distance_tree:          sf_treep;
  821.       lit_tree_present:       boolean;
  822.       eightK_dictionary:      boolean;
  823.       minimum_match_length:   integer;
  824.       dict_bits:              integer;
  825.  
  826.  
  827.    (* ----------------------------------------------------------- *)
  828.    procedure LoadTree(var tree: sf_treep;
  829.                       treesize: integer);
  830.       {allocate and load a shannon-fano tree from the compressed file}
  831.  
  832.       procedure SortLengths;
  833.          {Sort the Bit Lengths in ascending order, while retaining the order
  834.           of the original lengths stored in the file}
  835.       var
  836.          x:       integer;
  837.          gap:     integer;
  838.          t:       sf_entry;
  839.          noswaps: boolean;
  840.          a,b:     word;
  841.  
  842.       begin
  843.          gap := treesize div 2;
  844.  
  845.          with tree^ do
  846.          repeat
  847.             repeat
  848.                noswaps := true;
  849.                for x := 0 to (treesize-1)-gap do
  850.                begin
  851.                   a := entry[x].BitLength;
  852.                   b := entry[x+gap].BitLength;
  853.                   if (a > b) or
  854.                      ((a = b) and (entry[x].Value > entry[x+gap].Value)) then
  855.                   begin
  856.                      t := entry[x];
  857.                      entry[x] := entry[x+gap];
  858.                      entry[x+gap] := t;
  859.                      noswaps := false;
  860.                   end;
  861.                end;
  862.             until noswaps;
  863.  
  864.             gap := gap div 2;
  865.          until gap < 1;
  866.       end;
  867.  
  868.  
  869.       procedure ReadLengths;
  870.       var
  871.          treeBytes:  integer;
  872.          i:          integer;
  873.          num,len:    integer;
  874.       begin
  875.          {get number of bytes in compressed tree}
  876.          ReadBits(8,treeBytes);
  877.          inc(treeBytes);
  878.          i := 0;
  879.          with tree^ do
  880.          begin
  881.             MaxLength := 0;
  882.  
  883.             {High 4 bits: Number of values at this bit length + 1. (1 - 16)
  884.              Low  4 bits: Bit Length needed to represent value + 1. (1 - 16)}
  885.             while treeBytes > 0 do
  886.             begin
  887.                ReadBits(4,len);  inc(len);
  888.                ReadBits(4,num);  inc(num);
  889.  
  890.                while num > 0 do
  891.                with entry[i] do
  892.                begin
  893.                   if len > MaxLength then
  894.                      MaxLength := len;
  895.                   BitLength := len;
  896.                   Value := i;
  897.                   inc(i);
  898.                   dec(num);
  899.                end;
  900.  
  901.                dec(treeBytes);
  902.             end;
  903.          end;
  904.       end;
  905.  
  906.       procedure GenerateTrees;
  907.          {Generate the Shannon-Fano trees}
  908.       var
  909.          Code:          word;
  910.          CodeIncrement: integer;
  911.          LastBitLength: integer;
  912.          i:             integer;
  913.       begin
  914.          Code := 0;
  915.          CodeIncrement := 0;
  916.          LastBitLength := 0;
  917.  
  918.          i := treesize - 1;   {either 255 or 63}
  919.          with tree^ do
  920.          while i >= 0 do
  921.          begin
  922.             inc(Code,CodeIncrement);
  923.             if entry[i].BitLength <> LastBitLength then
  924.             begin
  925.                LastBitLength := entry[i].BitLength;
  926.                CodeIncrement := 1 shl (16 - LastBitLength);
  927.             end;
  928.  
  929.             entry[i].Code := Code;
  930.             dec(i);
  931.          end;
  932.       end;
  933.  
  934.       procedure ReverseBits;
  935.          {Reverse the order of all the bits in the above ShannonCode[]
  936.           vector, so that the most significant bit becomes the least
  937.           significant bit. For example, the value 0x1234 (hex) would become
  938.           0x2C48 (hex).}
  939.       var
  940.          i:    integer;
  941.          mask: word;
  942.          revb: word;
  943.          v:    word;
  944.          o:    word;
  945.          b:    integer;
  946.  
  947.       begin
  948.          for i := 0 to treesize-1 do
  949.          begin
  950.             {get original code}
  951.             o := tree^.entry[i].Code;
  952.  
  953.             {reverse each bit}
  954.             mask := $0001;
  955.             revb := $8000;
  956.             v := 0;
  957.             for b := 0 to 15 do
  958.             begin
  959.                {if bit set in mask, then substitute reversed bit}
  960.                if (o and mask) <> 0 then
  961.                   v := v or revb;
  962.  
  963.                {advance to next bit}
  964.                revb := revb shr 1;
  965.                mask := mask shl 1;
  966.             end;
  967.  
  968.             {store reversed bits}
  969.             tree^.entry[i].Code := v;
  970.          end;
  971.       end;
  972.  
  973.    begin
  974.       dos_getmem(tree,sizeof(tree^));
  975.       tree^.entries := treesize;
  976.       ReadLengths;
  977.       SortLengths;
  978.       GenerateTrees;
  979.       ReverseBits;
  980.    end;
  981.  
  982.  
  983.    (* ----------------------------------------------------------- *)
  984.    procedure LoadTrees;
  985.    begin
  986.       eightK_dictionary := (cflags and GP_8k_dict)  <> 0;
  987.       lit_tree_present  := (cflags and GP_lit_tree) <> 0;
  988.  
  989.       if eightK_dictionary then
  990.          dict_bits := 7
  991.       else
  992.          dict_bits := 6;
  993.  
  994.       if lit_tree_present then
  995.       begin
  996.          minimum_match_length := 3;
  997.          LoadTree(lit_tree,256);
  998.       end
  999.       else
  1000.          minimum_match_length := 2;
  1001.  
  1002.       LoadTree(length_tree,64);
  1003.       LoadTree(distance_tree,64);
  1004.    end;
  1005.  
  1006.  
  1007.    (* ----------------------------------------------------------- *)
  1008.    procedure ReadTree(tree: sf_treep;
  1009.                       var dest: integer);
  1010.       {read next byte using a shannon-fano tree}
  1011.    var
  1012.       bits: integer;
  1013.       cv:   word;
  1014.       b:    integer;
  1015.       cur:  integer;
  1016.  
  1017.    begin
  1018.       bits := 0;
  1019.       cv := 0;
  1020.       cur := 0;
  1021.       dest := -1; {in case of error}
  1022.  
  1023.       with tree^ do
  1024.       while true do
  1025.       begin
  1026.          ReadBits(1,b);
  1027.          cv := cv or (b shl bits);
  1028.          inc(bits);
  1029.  
  1030.          while entry[cur].BitLength < bits do
  1031.          begin
  1032.             inc(cur);
  1033.             if cur >= entries then
  1034.                exit;
  1035.          end;
  1036.  
  1037.          while entry[cur].BitLength = bits do
  1038.          begin
  1039.             if entry[cur].Code = cv then
  1040.             begin
  1041.                dest := entry[cur].Value;
  1042.                exit;
  1043.             end;
  1044.  
  1045.             inc(cur);
  1046.             if cur >= entries then
  1047.                exit;
  1048.          end;
  1049.       end;
  1050.  
  1051.    end;
  1052.  
  1053.  
  1054. (* ----------------------------------------------------------- *)
  1055. var
  1056.    lout:       integer;
  1057.    mem:        longint;
  1058.    op:         longint;
  1059.    Length:     integer;
  1060.    Distance:   integer;
  1061.    i:          integer;
  1062.  
  1063. begin
  1064.    mem := (sizeof(sf_tree)*3+100) - dos_maxavail;
  1065.    if mem > 0 then
  1066.    begin
  1067.       displn(ltoa(mem)+' more bytes of RAM needed to UnImplode!');
  1068.       skip_csize;
  1069.       exit;
  1070.    end;
  1071.  
  1072.    LoadTrees;
  1073.  
  1074.    while (not zipeof) and (outpos < cusize) and (not dump_user) do
  1075.    begin
  1076.       ReadBits(1,lout);
  1077.  
  1078.       if lout <> 0 then    {encoded data is literal data}
  1079.       begin
  1080.          if lit_tree_present then
  1081.             ReadTree(lit_tree,lout)   {use Literal Shannon-Fano tree}
  1082.          else
  1083.             ReadBits(8,lout);
  1084.  
  1085.          OutByte(lout);
  1086.       end
  1087.       else
  1088.  
  1089.       begin          {encoded data is sliding dictionary match}
  1090.          readBits(dict_bits,lout);
  1091.          Distance := lout;
  1092.  
  1093.          ReadTree(distance_tree,lout);
  1094.          Distance := Distance or (lout shl dict_bits);
  1095.          {using the Distance Shannon-Fano tree, read and decode the
  1096.             upper 6 bits of the Distance value}
  1097.  
  1098.          ReadTree(length_tree,Length);
  1099.          {using the Length Shannon-Fano tree, read and decode the Length value}
  1100.  
  1101.          inc(Length,Minimum_Match_Length);
  1102.          if Length = (63 + Minimum_Match_Length) then
  1103.          begin
  1104.             ReadBits(8,lout);
  1105.             inc(Length,lout);
  1106.          end;
  1107.  
  1108.          {move backwards Distance+1 bytes in the output stream, and copy
  1109.           Length characters from this position to the output stream.
  1110.           (if this position is before the start of the output stream,
  1111.           then assume that all the data before the start of the output
  1112.           stream is filled with zeros)}
  1113.  
  1114.          op := outpos - Distance - 1;
  1115.          for i := 1 to Length do
  1116.          begin
  1117.             if op < 0 then
  1118.                OutByte(0)
  1119.             else
  1120.                OutByte(outbuf[op mod sizeof(outbuf)]);
  1121.             inc(op);
  1122.          end;
  1123.       end;
  1124.    end;
  1125.  
  1126.    if lit_tree_present then
  1127.       dos_freemem(lit_tree);
  1128.    dos_freemem(distance_tree);
  1129.    dos_freemem(length_tree);
  1130. end;
  1131.  
  1132.  
  1133.  
  1134. (* ---------------------------------------------------------- *)
  1135. (*
  1136.  * This procedure displays the text contents of a specified archive
  1137.  * file.  The filename must be fully specified and verified.
  1138.  *
  1139.  *)
  1140. procedure viewfile;
  1141. var
  1142.    b: byte;
  1143.  
  1144. begin
  1145.    newline;
  1146.    {default_color;}
  1147.    binary_count := 0;
  1148.    pcbits := 0;
  1149.    incnt := 0;
  1150.    outpos := 0;
  1151.    uoutbuf := '';
  1152.    zipeof := false;
  1153.  
  1154.    if (cflags and GP_encrypted) <> 0 then
  1155.    begin
  1156.       displn('File is encrypted.');
  1157.       skip_csize;
  1158.       exit;
  1159.    end;
  1160.  
  1161.    case cmethod of
  1162.       0:    {stored}
  1163.             while (not zipeof) and (not dump_user) do
  1164.             begin
  1165.                ReadByte(b);
  1166.                OutByte(b);
  1167.             end;
  1168.  
  1169.       1:    UnShrink;
  1170.  
  1171.       2..5: UnReduce;
  1172.  
  1173.       6:    UnImplode;
  1174.  
  1175.       else  begin
  1176.                displn('Unknown compression method.');
  1177.                skip_csize;
  1178.             end;
  1179.    end;
  1180.  
  1181.    if nomore=false then
  1182.       newline;
  1183.  
  1184.    linenum := 1;
  1185. end;
  1186.  
  1187.  
  1188. (* ---------------------------------------------------------- *)
  1189. procedure _itoa(i: integer; var sp);
  1190. var
  1191.    s: array[1..2] of char absolute sp;
  1192. begin
  1193.    s[1] := chr( (i div 10) + ord('0'));
  1194.    s[2] := chr( (i mod 10) + ord('0'));
  1195. end;
  1196.  
  1197. function format_date(date: word): string8;
  1198. const
  1199.    s:       string8 = 'mm-dd-yy';
  1200. begin
  1201.    _itoa(((date shr 9) and 127)+80, s[7]);
  1202.    _itoa( (date shr 5) and 15,  s[1]);
  1203.    _itoa( (date      ) and 31,  s[4]);
  1204.    format_date := s;
  1205. end;
  1206.  
  1207. function format_time(time: word): string8;
  1208. const
  1209.    s:       string8 = 'hh:mm:ss';
  1210. begin
  1211.    _itoa( (time shr 11) and 31, s[1]);
  1212.    _itoa( (time shr  5) and 63, s[4]);
  1213.    _itoa( (time shl  1) and 63, s[7]);
  1214.    format_time := s;
  1215. end;
  1216.  
  1217.  
  1218. (* ---------------------------------------------------------- *)
  1219. procedure process_local_file_header;
  1220. var
  1221.    n:             word;
  1222.    rec:           local_file_header;
  1223.    filename:      string;
  1224.    extra:         string;
  1225.    fpos:          longint;
  1226.  
  1227. begin
  1228.    dos_lseek(zipfd,0,seek_cur);
  1229.    fpos := dos_tell;
  1230.  
  1231.    while (dump_user = false) do
  1232.    begin
  1233.       set_function(fun_arcview);
  1234.  
  1235.       dos_lseek(zipfd,fpos,seek_start);
  1236.       n := dos_read(zipfd,rec,sizeof(rec));
  1237.       get_string(rec.filename_length,filename);
  1238.       filename := remove_path(filename);
  1239.       stoupper(filename);
  1240.       get_string(rec.extra_field_length,extra);
  1241.       csize := rec.compressed_size;
  1242.       cusize := rec.uncompressed_size;
  1243.       cmethod := rec.compression_method;
  1244.       cflags := rec.general_purpose_bit_flag;
  1245.  
  1246.  
  1247.       (* exclude the file if outside current pattern *)
  1248.       if nomore or (not wildcard_match(pattern,filename)) then
  1249.       begin
  1250.          skip_csize;
  1251.          exit;
  1252.       end;
  1253.  
  1254.       (* display file information headers if needed *)
  1255.       if not header_present then
  1256.       begin
  1257.          header_present := true;
  1258.  
  1259.          newline;
  1260.          disp(' File Name    Length   Method     Date      Time');
  1261.          if expand_files then disp('    (Enter) or (S)kip, (V)iew');
  1262.          newline;
  1263.  
  1264.          disp('------------  ------  --------  --------  --------');
  1265.          if expand_files then disp('  -------------------------');
  1266.          newline;
  1267.       end;
  1268.  
  1269.  
  1270.       (* display file information *)
  1271.       disp(ljust(filename,12)+' '+
  1272.            rjust(ltoa(rec.uncompressed_size),7)+'  '+
  1273.            compression_methods[rec.compression_method]+'  '+
  1274.            format_date(rec.last_mod_file_date)+'  '+
  1275.            format_time(rec.last_mod_file_time));
  1276.  
  1277.       if not expand_files then
  1278.       begin
  1279.          skip_csize;
  1280.          newline;
  1281.          exit;
  1282.       end;
  1283.  
  1284.  
  1285.       (* determine action to perform on this member file *)
  1286.       action := 'S';
  1287.       disp('  Action? ');
  1288.       input(action,1);
  1289.       stoupper(action);
  1290.  
  1291.       case action[1] of
  1292.          'S':
  1293.             begin
  1294.                displn(' [Skip]');
  1295.                skip_csize;
  1296.                exit;
  1297.             end;
  1298.  
  1299.          'V','R':
  1300.             begin
  1301.                displn(' [View]');
  1302.                viewfile;
  1303.  
  1304.                header_present := false;
  1305.  
  1306.             {  make_log_entry('View archive member ('+extname
  1307.                                         +') from ('+remove_path(arcname)
  1308.                                         +')',true); }
  1309.             end;
  1310.  
  1311.          'Q':
  1312.             begin
  1313.                displn(' [Quit]');
  1314.                dos_lseek(zipfd,0,seek_end);
  1315.                exit;
  1316.             end;
  1317.  
  1318.          else
  1319.             displn(' [Type S, V or Q!]');
  1320.       end;
  1321.    end;
  1322. end;
  1323.  
  1324.  
  1325. (* ---------------------------------------------------------- *)
  1326. procedure process_central_file_header;
  1327. var
  1328.    n:             word;
  1329.    rec:           central_directory_file_header;
  1330.    filename:      string;
  1331.    extra:         string;
  1332.    comment:       string;
  1333.  
  1334. begin
  1335.    n := dos_read(zipfd,rec,sizeof(rec));
  1336.    get_string(rec.filename_length,filename);
  1337.    get_string(rec.extra_field_length,extra);
  1338.    get_string(rec.file_comment_length,comment);
  1339.   {dos_lseek(zipfd,rec.compressed_size,seek_cur);}
  1340. end;
  1341.  
  1342.  
  1343. (* ---------------------------------------------------------- *)
  1344. procedure process_end_central_dir;
  1345. var
  1346.    n:             word;
  1347.    rec:           end_central_dir_record;
  1348.    comment:       string;
  1349.  
  1350. begin
  1351.    n := dos_read(zipfd,rec,sizeof(rec));
  1352.    get_string(rec.zipfile_comment_length,comment);
  1353. end;
  1354.  
  1355.  
  1356. (* ---------------------------------------------------------- *)
  1357. procedure process_headers;
  1358. var
  1359.    sig:  longint;
  1360.  
  1361. begin
  1362.    dos_lseek(zipfd,0,seek_start);
  1363.    header_present := false;
  1364.  
  1365.    while (not dump_user) do
  1366.    begin
  1367.       if nomore or (dos_read(zipfd,sig,sizeof(sig)) <> sizeof(sig)) then
  1368.          exit
  1369.       else
  1370.  
  1371.       if sig = local_file_header_signature then
  1372.          process_local_file_header
  1373.       else
  1374.  
  1375.       if sig = central_file_header_signature then
  1376.          process_central_file_header
  1377.       else
  1378.  
  1379.       if sig = end_central_dir_signature then
  1380.       begin
  1381.          process_end_central_dir;
  1382.          exit;
  1383.       end
  1384.  
  1385.       else
  1386.       begin
  1387.          displn('Invalid Zipfile Header');
  1388.          exit;
  1389.       end;
  1390.    end;
  1391.  
  1392. end;
  1393.  
  1394.  
  1395. (* ---------------------------------------------------------- *)
  1396. procedure select_pattern;
  1397. begin
  1398.    default_pattern := '*.*';
  1399.  
  1400.    while true do
  1401.    begin
  1402.       newline;
  1403.       disp(remove_path(zipfn));
  1404.       get_def(': View member filespec:', enter_eq+default_pattern+'? ');
  1405.       
  1406.       get_nextpar;
  1407.       pattern := par;
  1408.       stoupper(pattern);
  1409.       if length(pattern) = 0 then
  1410.          pattern := default_pattern;
  1411.  
  1412.       if (pattern = 'none') or (pattern = 'Q') or dump_user then
  1413.          exit;
  1414.    
  1415.       process_headers;
  1416.    
  1417.       default_pattern := 'none';
  1418.    end;
  1419. end;
  1420.  
  1421.  
  1422. (* ---------------------------------------------------------- *)
  1423. procedure view_zipfile;
  1424. begin
  1425.    zipfd := dos_open(zipfn,open_read);
  1426.    if zipfd = dos_error then
  1427.       exit;
  1428.  
  1429.    if expand_files then
  1430.       select_pattern
  1431.    else
  1432.    begin
  1433.       pattern := '*.*';
  1434.       process_headers;
  1435.    end;
  1436.  
  1437.    dos_close(zipfd);
  1438. end;
  1439.  
  1440.  
  1441.  
  1442. (* ---------------------------------------------------------- *)
  1443. procedure process_zipfile(name: filenames);
  1444. var
  1445.    mem:    longint;
  1446.  
  1447. begin
  1448.    linenum := 1;
  1449.    cmdline := '';
  1450.    expand_files := false;
  1451.    zipfn := name;
  1452.    view_zipfile;
  1453.  
  1454.    newline;
  1455.    get_def('View text files in this zipfile:','(Enter)=yes? ');
  1456.  
  1457.    (* process text viewing if desired *)
  1458.    get_nextpar;
  1459.    if par[1] <> 'N' then
  1460.    begin
  1461.       expand_files := true;
  1462.       view_zipfile;
  1463.    end;
  1464. end;
  1465.  
  1466.  
  1467. (*
  1468.  * main program
  1469.  *
  1470.  *)
  1471.  
  1472. var
  1473.    i:    integer;
  1474.    n:    integer;
  1475.    par:  anystring;
  1476.  
  1477. begin
  1478.    gotoxy(60,scroll_line+1);
  1479.    reverseVideo;
  1480.    disp(' ZipTV ');
  1481.  
  1482.    SetScrollPoint(scroll_line);
  1483.    gotoxy(1,23);  lowVideo;
  1484.    linenum := 1;
  1485.  
  1486.    if paramcount = 0 then
  1487.    begin
  1488.       displn(version);
  1489. {     newline;
  1490.       displn('Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.');
  1491.       newline;  }
  1492.  
  1493.       displn('Usage:  ziptv [-Pport] [-Tminutes] [-Llines] [-Mlines] FILE[.zip]');
  1494.  
  1495. {     newline;
  1496.       displn('-Pn   enables com port COMn and monitors carrier');
  1497.       displn('-Tn   allows user to stay in program for n minutes');
  1498.       displn('-Ln   sets lines per screen');
  1499.       displn('-Mn   sets maximum lines per session');
  1500. }
  1501.       halt;
  1502.    end;
  1503.  
  1504.    for i := 1 to paramcount do
  1505.    begin
  1506.       par := paramstr(i);
  1507.       n := atoi(copy(par,3,5));
  1508.  
  1509.       if par[1] = '-' then
  1510.          case upcase(par[2]) of
  1511.             'P':  opencom(n);
  1512.             'T':  tlimit := n;      {time limit}
  1513.             'L':  user.pagelen := n;
  1514.             'M':  maxlines := n;
  1515.          end
  1516.       else
  1517.  
  1518.       begin
  1519.         if pos('.',par) = 0 then
  1520.             par := par + '.ZIP';
  1521.  
  1522.         if dos_exists(par) then
  1523.             process_zipfile(par)
  1524.         else
  1525.             displn('File not found: '+par);
  1526.       end;
  1527.    end;
  1528.  
  1529.    newline;
  1530.    displn(version);
  1531.    closecom;
  1532. end.
  1533.  
  1534.  
  1535.